package com.luo.demo.oidc.resource.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.function.Supplier;

/**
 * API 资源鉴权服务
 *
 * @author luohq
 * @version 1.0.0
 * @date 2022-02-25 13:31
 */
@Component
@Slf4j
public class ResourceApiAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {

    private Map<String, List<String>> scope2ApiPathMap = new HashMap<>(1);

    @PostConstruct
    private void init() {
        this.scope2ApiPathMap.put("articles.read", Arrays.asList("/api/articles", "/api/articles/post"));
    }


    @Override
    public AuthorizationDecision check(Supplier<Authentication> authSupplier, RequestAuthorizationContext authContext) {
        Authentication authentication = authSupplier.get();
        //若验证未通过，或者 没经过JWT Token验证，则认为验证失败（避免AnonymousAuthenticationToken用户验证通过）
        if (!authentication.isAuthenticated() || !(authentication instanceof JwtAuthenticationToken)) {
            return new AuthorizationDecision(false);
        }

        //获取JWT accessToken相关信息
        JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
        String userName = jwtAuthenticationToken.getName();
        //principal、credentials、token均对应Jwt
        Jwt jwt = jwtAuthenticationToken.getToken();
        List<String> scope = jwt.getClaimAsStringList("scope");
        Collection<GrantedAuthority> authorities = jwtAuthenticationToken.getAuthorities();
        String method = authContext.getRequest().getMethod();
        String requestUri = authContext.getRequest().getRequestURI();
        log.info("\ncur request:{} {} from user:{} \nwith scope:{} and authorities:{} \naccessToken: {}",
                method, requestUri, userName,
                scope, authorities,
                jwt.getTokenValue());

        //TODO 具体鉴权操作
        Boolean allowAccess = scope.stream()
                .filter(this.scope2ApiPathMap::containsKey)
                .filter(curScope -> this.scope2ApiPathMap.get(curScope).contains(requestUri))
                .findAny()
                .isPresent();

        return new AuthorizationDecision(allowAccess);
    }
}
